package com.hardson.toolkit.kaptcha;

import com.google.code.kaptcha.impl.DefaultKaptcha;
import com.google.code.kaptcha.util.Config;
import com.hardson.toolkit.util.IDUtils;
import com.hardson.toolkit.util.StrUtil;
import lombok.AllArgsConstructor;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.*;

/**
 * 谷歌kaptcha图片验证码工具类
 * 因其相貌不扬，已经被市场抛弃
 *
 * @author hardson
 */
@Deprecated
public class KaptchaUtil {
    private DefaultKaptcha kaptcha = null;
    private Map<String, KaptchaBean> validatePool = null;

    private static KaptchaUtil inst = null;
    private Timer timer = null;

    private KaptchaUtil() {
    }

    /**
     * 初始化并启用
     */
    public static void initialize() {
        inst = new KaptchaUtil();
        inst.validatePool = new HashMap<>();
        inst.kaptcha = inst.defaultConfig();
        inst.timer = new Timer(true);
        // 每5秒运行一次
        inst.timer.schedule(new AutoCleanTask(), 1000, 5000);
    }

    /**
     * kaptcha.border 是否有边框默认为true 我们可以自己设置yes，no
     * <p>
     * kaptcha.border.color 边框颜色默认为Color.BLACK
     * <p>
     * kaptcha.border.thickness 边框粗细度默认为1
     * <p>
     * kaptcha.producer.impl 验证码生成器默认为DefaultKaptcha
     * <p>
     * kaptcha.textproducer.impl 验证码文本生成器默DefaultTextCreator
     * <p>
     * kaptcha.textproducer.char.string 验证码文本字符内容范围默认为abcde2345678gfynmnpwx
     * <p>
     * kaptcha.textproducer.char.length 验证码文本字符长度默认为5
     * <p>
     * kaptcha.textproducer.font.names 验证码文本字体样式默认为new Font("Arial", 1, fontSize),
     * new Font("Courier", 1, fontSize)
     * <p>
     * kaptcha.textproducer.font.size 验证码文本字符大小默认为40
     * <p>
     * kaptcha.textproducer.font.color 验证码文本字符颜色默认为Color.BLACK
     * <p>
     * kaptcha.textproducer.char.space 验证码文本字符间距默认为2
     * <p>
     * kaptcha.noise.impl 验证码噪点生成对象默认为DefaultNoise
     * <p>
     * kaptcha.noise.color 验证码噪点颜色默认为Color.BLACK
     * <p>
     * kaptcha.obscurificator.impl 验证码样式引擎默认为WaterRipple
     * <p>
     * kaptcha.word.impl 验证码文本字符渲染默认为DefaultWordRenderer
     * <p>
     * kaptcha.background.impl 验证码背景生成器默认为DefaultBackground
     * <p>
     * kaptcha.background.clear.from 验证码背景颜色渐进默认为Color.LIGHT_GRAY
     * <p>
     * kaptcha.background.clear.to 验证码背景颜色渐进默认为Color.WHITE
     * <p>
     * kaptcha.image.width 验证码图片宽度默认为200
     * <p>
     * kaptcha.image.height 验证码图片高度默认为50
     *
     * @return
     */
    private DefaultKaptcha defaultConfig() {
        Properties properties = new Properties();
        properties.put("kaptcha.textproducer.char.length", "4");
        properties.put("kaptcha.border", "no");// 图片边框
        properties.put("kaptcha.textproducer.font.color", "darkGray");// 字体颜色,可以是这种格式"r,g,b"
        properties.put("kaptcha.noise.color", "darkGray");// 燥点颜色
        // properties.put("kaptcha.border.color", "red");// 图片边框颜色
        properties.put("kaptcha.textproducer.char.space", "4");// 验证码文本字符间距默认为2
        properties.put("kaptcha.image.width", "150");// 验证码图片宽度默认为200
        properties.put("kaptcha.image.height", "55");// 验证码图片高度默认为50

        Config config = new Config(properties);
        DefaultKaptcha defaultKaptcha = new DefaultKaptcha();
        defaultKaptcha.setConfig(config);
        return defaultKaptcha;
    }

    public static void config(Properties prop) {
        Config config = new Config(prop);
        DefaultKaptcha defaultKaptcha = new DefaultKaptcha();
        defaultKaptcha.setConfig(config);
        inst.kaptcha = defaultKaptcha;
    }

    public static void config(String key, String value) {
        if (key == null || (key = key.trim()).length() == 0) {
            throw new IllegalArgumentException("key值为空.");
        }
        Config config = inst.kaptcha.getConfig();
        if (config == null) {
            inst.kaptcha = inst.defaultConfig();
            inst.kaptcha.getConfig();
        }
        Properties prop = config.getProperties();
        prop.put(key, value);
    }

    /**
     * 生成认证码及图片并保存在缓存池中
     *
     * @param key
     * @return
     */
    public static synchronized KaptchaVo prepare() {
        if (inst == null) {
            initialize();
        }
        return inst.doPrepare();
    }

    /**
     * 生成认证码及图片并保存在缓存池中
     *
     * @param key
     * @return
     */
    private KaptchaVo doPrepare() {
        // 生成文字验证码
        String text = this.kaptcha.createText();
        // 生成图片验证码
        BufferedImage image = this.kaptcha.createImage(text);
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        try {
            ImageIO.write(image, "png", out);
        } catch (IOException e) {
            e.printStackTrace();
        }
        KaptchaBean katchaBean = KaptchaBean.of(text, System.currentTimeMillis());
        // 令牌
        String token = IDUtils.getUUID();
        this.validatePool.put(token, katchaBean);
        return KaptchaVo.of(token, "", out.toByteArray());
    }

    /**
     * 校验登录认证码
     *
     * @param token
     * @param text
     * @return
     */
    public static boolean validate(String token, String text) {
        return inst.doValidate(token, text);
    }

    /**
     * 校验登录认证码
     *
     * @param token
     * @param text
     * @return
     */
    private boolean doValidate(String token, String text) {
        if (StrUtil.isTrimEmpty(token) || Objects.toString(text, "").length() != 4) {
            return false;
        }
        KaptchaBean katchaBean = this.validatePool.get(token);
        return (katchaBean != null && text.equalsIgnoreCase(katchaBean.text));
    }

    @AllArgsConstructor(staticName = "of")
    private static class KaptchaBean {
        String text = "";
        long regTime;
    }

    /**
     * 检查并清理过期的认证码
     */
    private void doCheckExpired() {
        for (Iterator<String> it = this.validatePool.keySet().iterator(); it.hasNext(); ) {
            String token = it.next();
            KaptchaBean value = this.validatePool.get(token);
            if (System.currentTimeMillis() - value.regTime > 2 * 60 * 1000) {// 有限期2分钟
                it.remove();
            }
        }
    }

    /**
     * 检查并清理过期的认证码
     */
    public static void checkExpired() {
        inst.doCheckExpired();
    }
}
